import { ComboboxDemos } from "@/lib/@docs/demos/src";
import { Layout } from "@/layout";
import { MDX_DATA } from "@/mdx";

export default Layout(MDX_DATA.Combobox);

## Examples

This page contains only a small set of examples, as the full code of the demos is long.
You can find all 50+ examples on a [separate page](/combobox?e=BasicSelect).

<ExamplesButton
  link="/combobox?e=BasicSelect"
  label="Open Combobox examples page"
/>

## Usage

`Combobox` provides a set of components and hooks to custom select, multiselect or autocomplete components.
The component is very flexible – you have full control of the rendering and logic.

<Demo data={ComboboxDemos.select} />

## useCombobox hook

`useCombobox` hook provides combobox store. The store contains the current state of the component
and handlers to update it. Created store must be passed to the `store` prop of `Combobox`:

```tsx
import { Combobox, useCombobox } from "@mantine/core";

function Demo() {
  const combobox = useCombobox();
  return <Combobox store={combobox}>{/* Your implementation */}</Combobox>;
}
```

## useCombobox options

`useCombobox` hooks accepts an options object with the following properties:

```tsx
interface UseComboboxOptions {
  /** Default value for `dropdownOpened`, `false` by default */
  defaultOpened?: boolean;

  /** Controlled `dropdownOpened` state */
  opened?: boolean;

  /** Called when `dropdownOpened` state changes */
  onOpenedChange?(opened: boolean): void;

  /** Called when dropdown closes with event source: keyboard, mouse or unknown */
  onDropdownClose?(eventSource: ComboboxDropdownEventSource): void;

  /** Called when dropdown opens with event source: keyboard, mouse or unknown */
  onDropdownOpen?(eventSource: ComboboxDropdownEventSource): void;

  /** Determines whether arrow key presses should loop though items (first to last and last to first), `true` by default */
  loop?: boolean;

  /** `behavior` passed down to `element.scrollIntoView`, `'instant'` by default */
  scrollBehavior?: ScrollBehavior;
}
```

You can import `UseComboboxOptions` type from `@mantine/core` package:

```tsx
import type { UseComboboxOptions } from "@mantine/core";
```

## Combobox store

Combobox store is an object with the following properties:

```tsx
interface ComboboxStore {
  /** Current dropdown opened state */
  dropdownOpened: boolean;

  /** Opens dropdown */
  openDropdown(eventSource?: "keyboard" | "mouse" | "unknown"): void;

  /** Closes dropdown */
  closeDropdown(eventSource?: "keyboard" | "mouse" | "unknown"): void;

  /** Toggles dropdown opened state */
  toggleDropdown(eventSource?: "keyboard" | "mouse" | "unknown"): void;

  /** Selected option index */
  selectedOptionIndex: number;

  /** Selects `Combobox.Option` by index */
  selectOption(index: number): void;

  /** Selects first `Combobox.Option` with `active` prop.
   *  If there are no such options, the function does nothing.
   */
  selectActiveOption(): string | null;

  /** Selects first `Combobox.Option` that is not disabled.
   *  If there are no such options, the function does nothing.
   * */
  selectFirstOption(): string | null;

  /** Selects next `Combobox.Option` that is not disabled.
   *  If the current option is the last one, the function selects first option, if `loop` is true.
   */
  selectNextOption(): string | null;

  /** Selects previous `Combobox.Option` that is not disabled.
   *  If the current option is the first one, the function selects last option, if `loop` is true.
   * */
  selectPreviousOption(): string | null;

  /** Resets selected option index to -1, removes `data-combobox-selected` from selected option */
  resetSelectedOption(): void;

  /** Triggers `onClick` event of selected option.
   *  If there is no selected option, the function does nothing.
   */
  clickSelectedOption(): void;

  /** Updates selected option index to currently selected or active option.
   *  The function is required to be used with searchable components to update selected option index
   *  when options list changes based on search query.
   */
  updateSelectedOptionIndex(target?: "active" | "selected"): void;

  /** List id, used for `aria-*` attributes */
  listId: string | null;

  /** Sets list id */
  setListId(id: string): void;

  /** Ref of `Combobox.Search` input */
  searchRef: React.MutableRefObject<HTMLInputElement | null>;

  /** Moves focus to `Combobox.Search` input */
  focusSearchInput(): void;

  /** Ref of the target element */
  targetRef: React.MutableRefObject<HTMLElement | null>;

  /** Moves focus to the target element */
  focusTarget(): void;
}
```

You can import `ComboboxStore` type from `@mantine/core` package:

```tsx
import type { ComboboxStore } from "@mantine/core";
```

## useCombobox handlers

Combobox store handlers can be used to control `Combobox` state.
For example, to open the dropdown, call `openDropdown` handler:

```tsx
import { Button, Combobox, useCombobox } from "@mantine/core";

function Demo() {
  const combobox = useCombobox();

  return (
    <Combobox>
      <Combobox.Target>
        <Button onClick={() => combobox.openDropdown()}>Open dropdown</Button>
      </Combobox.Target>

      {/* Your implementation */}
    </Combobox>
  );
}
```

You can use store handlers in `useCombobox` options. For example, you can
call `selectFirstOption` when the dropdown is opened and `resetSelectedOption`
when it is closed:

```tsx
import { Combobox, useCombobox } from "@mantine/core";

function Demo() {
  const combobox = useCombobox({
    onDropdownOpen: () => combobox.selectFirstOption(),
    onDropdownClose: () => combobox.resetSelectedOption(),
  });

  return <Combobox store={combobox}>{/* Your implementation */}</Combobox>;
}
```

## Combobox.Target

`Combobox.Target` should be used as a wrapper for the target element or component.
`Combobox.Target` marks its child as a target for dropdown and sets `aria-*` attributes
and adds keyboard event listeners to it.

`Combobox.Target` requires a single child element or component. The child component
must accept `ref` and `...others` props. You can use any Mantine component as a target without
any additional configuration, for example, [Button](/core/button/), [TextInput](/core/text-input/)
or [InputBase](/core/input/#inputbase-component).

Example of using `Combobox.Target` with [TextInput](/core/text-input/) component:

<Demo data={ComboboxDemos.autocomplete} />

Example of using `Combobox.Target` with [Button](/core/button) component:

<Demo data={ComboboxDemos.button} />

## Split events and dropdown targets

In some cases, you might need to use different elements as an events target and as a dropdown.
Use `Combobox.EventsTarget` to add `aria-*` attributes and keyboard event handlers, and
`Combobox.DropdownTarget` to position the dropdown relative to the target.

You can have as many `Combobox.EventsTarget` as you need, but only one `Combobox.DropdownTarget`
per `Combobox`.

Example of using `Combobox.EventsTarget` and `Combobox.DropdownTarget` with [PillsInput](/core/pills-input) component
to create a searchable multiselect component:

<Demo data={ComboboxDemos.searchableMultiselect} />

## Update selected option index

`updateSelectedOptionIndex` handler is required to be called when options list changes.
Usually, the options list changes when options are filtered based on the search query. In this case,
you need to call `updateSelectedOptionIndex` in `onChange` handler of the search input.

Example of using `updateSelectedOptionIndex` handler in searchable select component:

<Demo data={ComboboxDemos.searchableSelect} />

## Search input

If you prefer search input inside the dropdown, use `Combobox.Search` component.
To focus the search input, call `combobox.focusSearchInput`, usually it is done
when the dropdown is opened. To prevent focus loss after the dropdown is closed,
call `combobox.focusTarget`:

<Demo data={ComboboxDemos.buttonSearch} />

## Select first option

Use `combobox.selectFirstOption` function to select the first option. It is useful
if you want to select the first option when user searching for options in the list.
If there are no options available, it will do nothing.

<Demo data={ComboboxDemos.selectFirstOption} />

## Active option

Set `active` prop on `Combobox.Option` component to mark it as active.
By default, an active option does not have any styles, you can use `data-combobox-active`
[data attribute](/styles/data-attributes) to style it.

`combobox.selectActiveOption` function selects active option. Usually, it is called
when the dropdown is opened:

<Demo data={ComboboxDemos.activeOption} />

## Options groups

Render `Combobox.Option` components inside `Combobox.Group` to create options group.
`Combobox.Group` label will be automatically hidden if the group does not have any
children.

<Demo data={ComboboxDemos.groups} />

## Scrollable list

Set `max-height` style on either `Combobox.Dropdown` or `Combobox.Options` to make the
options list scrollable. You can use `mah` [style prop](/styles/style-props) to set
`max-height`.

<Demo data={ComboboxDemos.nativeScroll} />

## Scrollable list with ScrollArea

You can also use [ScrollArea or ScrollArea.Autosize](/core/scroll-area) components
instead of native scrollbars:

<Demo data={ComboboxDemos.scrollArea} />

## Hide dropdown

Set `hidden` prop on `Combobox.Dropdown` to hide the dropdown. For example,
it can be useful when you want to show the dropdown only when there is at least
one option available:

<Demo data={ComboboxDemos.hiddenDropdown} />

## Control dropdown opened state

To control the dropdown opened state, pass `opened` to `useCombobox` hook:

<Demo data={ComboboxDemos.controlledDropdown} />

## Popover props

`Combobox` supports most of [Popover](/core/popover) props. For example,
you can control dropdown position with `position` prop and disable Floating UI
middlewares with `middlewares` prop:

<Demo data={ComboboxDemos.dropdownPosition} />

## Without dropdown

You can use `Combobox` without dropdown. To do so, use `Combobox.EventsTarget` instead
of `Combobox.Target`:

<Demo data={ComboboxDemos.noDropdown} />

<StylesApiSelectors component="Combobox" />

<Demo data={ComboboxDemos.stylesApi} />
